#include "ServerThread.h"
#include "SocketImpl.h"
#include "Constants.h"
#include "ClientThread.h"

#include <log4cpp/Category.hh>
#include <memory>
#include <algorithm>


namespace ServerThread
{

log4cpp::Category& logger =
    log4cpp::Category::getInstance(std::string(SERVER_LOGGER));

ServerThread::ServerThread():_timeToExit(false), _serverFD(0),
    _ThreadPool(new ThreadPool::ThreadPool(5)), _msgCount(0){//TODO

}

ServerThread::~ServerThread() {

}

void ServerThread::setExitFlag() {
  _timeToExit = true;
}

bool ServerThread::exit() {
  return _timeToExit.load();
}

void ServerThread::msgStatus(int msgCount){
  std::unique_lock<std::mutex> lock(_counterMutex);
  _msgCount+=msgCount;

}

unsigned long ServerThread::msgCount(){
  std::unique_lock<std::mutex> lock(_counterMutex);
  return (_msgCount);
}

void ServerThread::operator()() {
  Constants::setThreadID(_threadID);

  try {
    setupThread();
  }
  catch(std::exception& ex) {
    LOG_ERROR(_threadID + ex.what());
    throw std::runtime_error(_threadID + ex.what());
  }
}

/**
 * This is open listening socket server by reading config file
 *
 */
void ServerThread::setupThread() {
  LOG_DEBUG(_threadID +"ServerThread::setupThread->In");

  _serverFD = SocketImp::createServerSocket();

  bool done = false;
  while(!done) {
    try {
      unsigned int clientPort = 0;
      std::string clientIP;
      int clientFD = SocketImp::getNewConnection(_serverFD, clientPort,
          clientIP);

      handleThread(clientFD);

      //Need to check for terminate flag
      if (exit()) {
        //then wait for all the client threads are done
        handleShutdown();
        break;
        //then exit
      }
    }
    catch(std::exception& ex) {
      handleShutdown();
      LOG_ERROR(_threadID+ex.what());
      throw std::runtime_error(_threadID+ex.what());
    }
  }
  LOG_DEBUG(_threadID+"ServerThread::setupThread->Out");
}

void ServerThread::handleShutdown() {

  LOG_DEBUG(_threadID+"ServerThread::handleShutdown->In");

  _ThreadPool.reset(nullptr);

  close(_serverFD);

  LOG_DEBUG(_threadID+"ServerThread::handleShutdown->Out");
}

void ServerThread::handleThread(int clientFD) {

  LOG_DEBUG(_threadID+"ServerThread::handleThread->In");
  ClientPtr clientThread(nullptr);
  try {
    //spawn new thread to handle
    LOG_DEBUG(_threadID+"before creating client thread");
    //clientThread.reset(new std::thread(ClientThread::ClientThread(clientFD)));

    ThreadPool::TaskPtr ptr(new ClientThread::ClientThread(clientFD, *this));
    _ThreadPool->addTask(std::move(ptr));

    LOG_DEBUG(_threadID+"after creating client thread");
  }
  catch(std::exception& ex) {
    LOG_ERROR(_threadID+ex.what());
    throw std::runtime_error(_threadID+ex.what());
  }

  LOG_DEBUG(_threadID+"ServerThread::handleThread->Out");

}


}
